﻿using System;
using Game2DFramework;
using Game2DFramework.Cameras;
using Game2DFramework.Collision;
using Game2DFramework.Extensions;
using MechGame.Game.Leveling;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MechGame.Game.PlayerRobot
{
    class Player : GameObject, IFollowingTarget
    {
        const float RotationDuration = 0.15f;

        private readonly Texture2D _lightBulb;
        private readonly Texture2D _lightBulb2;
        private readonly Texture2D _head;
        private readonly Texture2D _body;
        private Level _currentLevel;
        private Vector2 _currentWalkDir;
        
        private readonly PrimaryWeapon _primaryWeapon;
        private readonly RocketWeapon _rocketWeapon;
        private readonly RotationInterpolation _rotationInterpolation;

        public Vector2 Position;
        
        public int Life { get; set; }
        public int MaxLife { get; set; }
        public float Speed { get; set; }
        public Vector2 CurrentLookDir { get; private set; }
        public float HeadRotation { get; private set; }
        public Circle Bounds { get; private set; }
        public int GateLevel { get; set; }

        public Player(Game2D game) : base(game)
        {
            Game.GlobalObjectChanged += OnGlobalObjectChanged;

            _head = Game.Content.Load<Texture2D>("Textures/head");
            _body = Game.Content.Load<Texture2D>("Textures/body");
            _lightBulb = Game.Content.Load<Texture2D>("Textures/circle");
            _lightBulb2 = Game.Content.Load<Texture2D>("Textures/bulb");
            _primaryWeapon = new PrimaryWeapon(this,0.25f, 300.0f);
            _rocketWeapon = new RocketWeapon(this, 300.0f, 1.0f);
            Speed = 200.0f;
            _currentWalkDir = -Vector2.UnitY;
            CurrentLookDir = -Vector2.UnitY;
            Bounds = new Circle((int) Position.X, (int) Position.Y, 20);

            _rotationInterpolation = new RotationInterpolation((float) Math.Atan2(_currentWalkDir.Y, _currentWalkDir.Y));

            Life = 100;
            MaxLife = 100;
        }

        private void OnGlobalObjectChanged(string name, GameObject oldValue, GameObject newValue)
        {
            if (name == "Level") _currentLevel = (Level)newValue;
        }

        private void HandleMove(float elapsed)
        {
            if (Game.ScriptRunner.LocksControls) return;
            if (LockControl) return;

            var newWalkDir = Vector2.Zero;

            if (Game.Keyboard.IsKeyDown(Keys.A)) newWalkDir.X = -1;
            else if (Game.Keyboard.IsKeyDown(Keys.D)) newWalkDir.X = 1;
            if (Game.Keyboard.IsKeyDown(Keys.W)) newWalkDir.Y = -1;
            else if (Game.Keyboard.IsKeyDown(Keys.S)) newWalkDir.Y = 1;

            Position = MoveTo(elapsed, Position + newWalkDir * elapsed * Speed);
            Bounds = new Circle((int)Position.X, (int)Position.Y, 20);

            _rotationInterpolation.Update(elapsed);
        }

        public void Update(float elapsed)
        {
            HandleMove(elapsed);
            HandleItemCollection();
            HandleLooking();
            HandleWeapons(elapsed);
        }

        private void HandleItemCollection()
        {
            Item item;
            if (!_currentLevel.TryCollectionItem(Bounds, out item)) return;
            switch(item.ItemType)
            {
                case AttachedItem.EnergyContainer:
                    MaxLife += 100;
                    Life = MaxLife;
                    ShowCollectedDialog();
                    break;
            }
        }

        private void ShowCollectedDialog()
        {
            Game.ScriptRunner
                .AddDialogStep("You Collect an Energy Container.")
                .AddDialogStep("You get +100 Energy.")
                .Start();
        }

        public void MoveToPosition(float elapsed, Vector2 position)
        {
            Position = MoveTo(elapsed, position, false);
        }

        private Vector2 MoveTo(float elapsed, Vector2 position, bool handleCollision = true)
        {
            var newPosition = Position;
            var newDirection = position - Position;
            if (newDirection != Vector2.Zero)
            {
                _currentWalkDir = Vector2.Normalize(newDirection);
                var distance = Speed * elapsed;
                if (newDirection.LengthSquared() <= distance * distance)
                    newPosition = position;
                else
                    newPosition += _currentWalkDir * Speed * elapsed;
            }

            var rotation = (float)Math.Atan2(_currentWalkDir.Y, _currentWalkDir.X) + MathHelper.PiOver2;
            _rotationInterpolation.SetTargetAngle(rotation, RotationDuration);

            var newX = newPosition.X;
            var newY = newPosition.Y;
            if (handleCollision)
            {
                if (_currentLevel.CollidesWith(new Circle((int)newX, (int)Position.Y, Bounds.Radius))) newX = Position.X;
                if (_currentLevel.CollidesWith(new Circle((int)Position.X, (int)newY, Bounds.Radius))) newY = Position.Y;
            }
            newPosition = new Vector2(newX, newY);

            return newPosition;
        }

        public bool LockControl { get; set; }

        private void HandleWeapons(float elapsedTime)
        {
            if (Game.ScriptRunner.LocksControls || LockControl) return;
            _primaryWeapon.Update();
            _rocketWeapon.Update(elapsedTime);
        }

        private void HandleLooking()
        {
            if (Game.ScriptRunner.LocksControls) return;
            if (LockControl) return;

            var mouseState = Mouse.GetState();
            CurrentLookDir = Vector2.Normalize(new Vector2(mouseState.X, mouseState.Y) + Game.Camera.Offset - Position);
            HeadRotation = (float)Math.Atan2(CurrentLookDir.Y, CurrentLookDir.X) + MathHelper.PiOver2;
        }

        public void DrawShape(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(_lightBulb, Position, null, Color.White, 0.0f, new Vector2(_lightBulb.Width * 0.5f),1.0f, SpriteEffects.None, 0);
            spriteBatch.Draw(_lightBulb2, Position, null, Color.White, HeadRotation, new Vector2(_lightBulb2.Width * 0.5f), 1.0f, SpriteEffects.None, 0);
        }

        public void Draw()
        {
            var position = Position.SnapToPixels();
            Game.SpriteBatch.Draw(_body, position, null, Color.White, _rotationInterpolation.CurrentAngle, new Vector2(_body.Width, _body.Height) * 0.5f, 0.6f, SpriteEffects.None, 0);
            Game.SpriteBatch.Draw(_head, position, null, Color.White, HeadRotation, new Vector2(_head.Width, _head.Height) * 0.5f, 0.6f, SpriteEffects.None, 0);
            _primaryWeapon.Draw();
        }

        public Vector2 FollowingPosition { get { return Position; }}
    }
}
